package falcon.client;

import java.util.List;

import falcon.client.FalconClient.RecycleListener;
import falcon.common.FalconConfig;
import falcon.common.Log;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

public final class FalconClientPool {

	public FalconClientPool(FalconConfig config) {
		List<String> hostList = config.getHostList();
		m_host = hostList.get(0);
		m_port = config.getPort();
		
		m_group = new NioEventLoopGroup();
		m_boot = new Bootstrap();
		m_boot.group(m_group);
		m_boot.channel(NioSocketChannel.class);
		m_boot.handler(new ChannelInitializer<SocketChannel>() {
			@Override
			protected void initChannel(SocketChannel arg0) throws Exception {
				arg0.pipeline().addLast(KEY, new FalconClient());
			}
		});
		m_counter = 0L;
		// 底层已经异步化，则不必建立过多连接
		// 池化的目的在于便于以后，多实例高可用拓展
		m_pool = new FalconClient[1];
		for (int i = 0; i < m_pool.length; i ++) {
			m_pool[i] = null;
		}
	}
	
	public FalconClientProxy get() {
		FalconClient client;
		synchronized (this) {
			int index = (int) (m_counter % m_pool.length);
			m_counter ++;
			client = m_pool[index];
			if (client == null || client.isClose()) {
				try {
					ChannelFuture future = m_boot.connect(m_host, m_port);
					client = (FalconClient) future.sync().channel().pipeline().get(KEY);
					ClientRecycleListener listener = new ClientRecycleListener(index, this);
					client.bindListener(listener);
					m_pool[index] = client;
				} catch (Exception e) {
					client = null;
					Log.log(e, "get proxy failure");
				}
			}
		}
		FalconClientProxy proxy = new FalconClientProxy(client);
		return proxy;
	}
	
	public void destory() {
		for (int i = 0; i < m_pool.length; i ++) {
			FalconClient client = m_pool[i];
			if (client == null || client.isClose()) {
				continue;
			}
			client.close();
		}
		m_group.shutdownGracefully();
	}
	
	private static final class ClientRecycleListener implements RecycleListener {

		public ClientRecycleListener(int idx, FalconClientPool pool) {
			m_idx = idx;
			m_pool = pool;
		}

		@Override
		public void recycle() {
			if (m_idx < 0 || m_idx >= m_pool.m_pool.length) {
				return;
			}
			synchronized (m_pool) {
				m_pool.m_pool[m_idx] = null;
			}
		}
		
		private int m_idx;
		private FalconClientPool m_pool;
	}
	
	private static final String KEY = FalconClient.class.getSimpleName();
	
	private String m_host;
	private int m_port;
	private EventLoopGroup m_group;
	private Bootstrap m_boot;
	private FalconClient[] m_pool;
	private volatile long m_counter;
}
